﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using EnvDTE;
using EnvDTE80;
using Expect.Core;
using Microsoft.VisualStudio.Shell.Interop;

namespace Expect.VisualStudio.Extensions.Commands.Handlers
{    
    class CreateSpecificationMethod : UnresolvedFixtureCommand
    {
        public CreateSpecificationMethod(IExpectPackage pkg)
            : base(pkg, CommandIDs.cmdidCreateSpecificationMethod)
        {
        }

        public override string DisplayText
        {
            get
            {
                return "Specification => create method";
            }
        }

        public override bool ShowCommand(SelectionResolution resolution)
        {
            if (!base.ShowCommand(resolution)) return false;

            if (resolution.Specification.Name == Path.GetFileNameWithoutExtension(resolution.Context.FileName)) 
                return false;

            return true;
        }

        public override void ExecuteCommand(SelectionResolution resolution)
        {
            var dte = (DTE2)this.GetVSService(typeof(SDTE));

            var specClass = GetSpecificationCodeClass(_pkg, resolution, dte);

            if (specClass == null) return;

            CodeFunction2 method = GetFixtureMethod(resolution, resolution.ClickedRow, specClass);

            var pt = method.GetStartPoint(vsCMPart.vsCMPartBody);

            var wnd = specClass.ProjectItem.Open();
            wnd.Activate();

            EditorHelper.SetCursorTo(dte, wnd, pt.Line, 0);
        }

        public static CodeFunction2 GetFixtureMethod(SelectionResolution resolution, FixtureRow row, CodeClass specclass)
        {
            var methodName = resolution.NameProvider.ElementName(row.NamesForFluent().ToArray());
            var methodArgs = row.ArgsForFluent().ToArray();

            return CreateContainerMethod.GetFixtureMethod(resolution, row, methodName, methodArgs, specclass);
        }

        public static CodeClass GetSpecificationCodeClass(IExpectPackage pkg, SelectionResolution resolution, EnvDTE80.DTE2 dte)
        {
            var specClassName = resolution.NameProvider.ElementName(resolution.Fixture.Parent.Name);
            if (resolution.SpecItem == null)
            {
                return GenerateSpecificationCodeClass(pkg, resolution, dte, specClassName);
            }

            return resolution.SpecItem.Item.FileCodeModel.CodeElements.GetClass(specClassName);
        }

        public static CodeClass GenerateSpecificationCodeClass(IExpectPackage pkg, SelectionResolution resolution, EnvDTE80.DTE2 dte, string specClassName)
        {
            var specFileDir = Path.GetDirectoryName(resolution.Context.FileName);
            var specFileName = Path.Combine(specFileDir, specClassName + ".cs");
            var specNamespace = resolution.Context.ContainerClassFullName.Substring(0, resolution.Context.ContainerClassFullName.LastIndexOf("."));
            var specFullName = specNamespace + "." + specClassName;

            Dictionary<string, List<ProjectItem>> classes = new Dictionary<string, List<ProjectItem>>();
            resolution.Context.Item.ContainingProject.GetProjectItemForClassName(classes, specFullName);
            if (classes.Count > 0)
            {
                CommandSet.ShowMessage(pkg, "Enable to create the specification class " + specFullName + " because a class with this name already exists");
                return null;
            }
            if (File.Exists(specFileName))
            {
                CommandSet.ShowMessage(pkg, "A file with path " + specFileName + " already exists");
                return null;
            }

            CodeModel cm = (CodeModel)resolution.Context.Item.ContainingProject.CodeModel;
            CodeNamespace currentnamespace = cm.AddNamespace(specNamespace, specFileName, -1);
            var edit = currentnamespace.GetStartPoint().CreateEditPoint();
            edit.Insert("using System;\r\n");

            object[] bases = { typeof(AbstractSpecImplementation).FullName };
            object[] interfaces = { /*"System.IDisposable", "System.IComparable"*/ };

            CodeClass generatedClass = (CodeClass)currentnamespace.AddClass(specClassName, -1, bases, interfaces, vsCMAccess.vsCMAccessPublic);
            
            return generatedClass;
        }
    }
}
